﻿using System;
using System.Collections.Generic;
using System.IO;
using CSJ2K.Util.IO;

namespace CSJ2K.Jpc.Decoder
{
    public class MQDecoder
    {
        /// <summary>The data structures containing the probabilities for the LPS </summary>
        static readonly uint[] Qe = { 0x5601, 0x3401, 0x1801, 0x0ac1, 0x0521, 0x0221, 0x5601, 0x5401, 0x4801, 0x3801, 0x3001, 0x2401, 0x1c01, 0x1601, 0x5601, 0x5401, 0x5101, 0x4801, 0x3801, 0x3401, 0x3001, 0x2801, 0x2401, 0x2201, 0x1c01, 0x1801, 0x1601, 0x1401, 0x1201, 0x1101, 0x0ac1, 0x09c1, 0x08a1, 0x0521, 0x0441, 0x02a1, 0x0221, 0x0141, 0x0111, 0x0085, 0x0049, 0x0025, 0x0015, 0x0009, 0x0005, 0x0001, 0x5601 };

        /// <summary>The indexes of the next MPS </summary>
        static readonly uint[] NMPS = { 1, 2, 3, 4, 5, 38, 7, 8, 9, 10, 11, 12, 13, 29, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 45, 46 };

        /// <summary>The indexes of the next LPS </summary>
        static readonly uint[] NLPS = { 1, 6, 9, 12, 29, 33, 6, 14, 14, 14, 17, 18, 20, 21, 14, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 46 };

        /// <summary>Whether LPS and MPS should be switched </summary>
        static readonly uint[] SWITCH = { 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

        static readonly uint[] NI = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46 };

        uint[] I = new uint[47];
        uint[] MPS = new uint[47];

        uint A;
        uint CT;
        uint C;

        Stream Source;

        public MQDecoder(Stream Source)
        {
            this.Source = Source;

            Array.Copy(NI, I, 47);
            Array.Copy(NMPS, MPS, 47);

            byte[] buf = new byte[1];
            if (Source.Read(buf, 0, 1) < 1) throw (new EndOfStreamException("Decoder couldn't read first byte."));
            uint B = buf[0];

            C = B << 16;

            ByteIn();

            C = C << 7;
            CT = CT - 7;
            A = 0x8000;
        }

        public uint Decode(int CX)
        {
            uint i = I[CX];
            uint qe = Qe[i];
            uint D;
       
            A -= qe;
            // This requires 2 instructions. It might be better to split C into Chigh/Clow.
            // C style unions would be best but the equivalent in C# requires the interop 
            // library or unsafe code which isn't available in SilverLight and probably
            // would be slower anyway.
            if ((C>>16) < qe) 
            {
                // LPS_EXCHANGE
                if (A < qe)
                {
                    A = qe;
                    D = MPS[CX];
                    i = I[CX] = NMPS[i];

                    // Single RENORMD
                    if (CT == 0) ByteIn();
                    A <<= 1;
                    C <<= 1;
                    CT--;
                }
                else
                {
                    A = qe;
                    D = 1 - MPS[CX];
                    if (SWITCH[i] == 1) MPS[CX] = 1 - MPS[CX];
                    i = I[CX] = NLPS[i];

                    // RENORMD
                    do
                    {

                        if (CT == 0) ByteIn();
                        A <<= 1;
                        C <<= 1;
                        CT--;
                    }
                    while (A >= 0x8000);
                }
            }
            else
            {
                C -= qe << 16;
                if (A >= 0x8000)  // Faster than (A & 0x8000) == 0
                {
                    // MPS_EXCHANGE
                    if (A < qe)
                    {
                        D = 1 - MPS[CX];
                        if (SWITCH[i]==1) MPS[CX] = 1 - MPS[CX];
                        i = I[CX] = NLPS[i];
                    }
                    else
                    {
                        D = MPS[CX];
                        i = I[CX] = NMPS[i];
                    }

                    // RENORMD
                    do
                    {
                        if (CT == 0) ByteIn();
                        A <<= 1;
                        C <<= 1;
                        CT--;
                    }
                    while (A >= 0x8000);
                }
                else
                {
                    D = MPS[CX];
                }
            }
            return D;
        }

        // Should this be inlined or is the runtime smart enough to do it for us?
        void ByteIn()
        {
            // Currently no need to check for markers due to the design of the Packet class.
            // This library currently assumes reliable transport other than possible truncation.
            byte[] buf = new byte[1];
            if(Source.Read(buf, 0, 1) < 1)
            {
                C += 0xFF00;
                CT = 8;
                return;
            }
            uint B=buf[0];
            C += B << 8;
            CT = 8;
        }
    }
}
